#ifndef XRPL_APP_CONSENSUSS_VALIDATIONS_H_INCLUDED
#define XRPL_APP_CONSENSUSS_VALIDATIONS_H_INCLUDED

#include <xrpld/app/ledger/Ledger.h>
#include <xrpld/consensus/Validations.h>

#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/RippleLedgerHash.h>
#include <xrpl/protocol/STValidation.h>

#include <optional>
#include <vector>

namespace ripple {

class Application;

enum class BypassAccept : bool { no = false, yes };

/** Wrapper over STValidation for generic Validation code

    Wraps an STValidation for compatibility with the generic validation code.
*/
class RCLValidation
{
    std::shared_ptr<STValidation> val_;

public:
    using NodeKey = ripple::PublicKey;
    using NodeID = ripple::NodeID;

    /** Constructor

        @param v The validation to wrap.
    */
    RCLValidation(std::shared_ptr<STValidation> const& v) : val_{v}
    {
    }

    /// Validated ledger's hash
    uint256
    ledgerID() const
    {
        return val_->getLedgerHash();
    }

    /// Validated ledger's sequence number (0 if none)
    std::uint32_t
    seq() const
    {
        return val_->getFieldU32(sfLedgerSequence);
    }

    /// Validation's signing time
    NetClock::time_point
    signTime() const
    {
        return val_->getSignTime();
    }

    /// Validated ledger's first seen time
    NetClock::time_point
    seenTime() const
    {
        return val_->getSeenTime();
    }

    /// Public key of validator that published the validation
    PublicKey
    key() const
    {
        return val_->getSignerPublic();
    }

    /// NodeID of validator that published the validation
    NodeID
    nodeID() const
    {
        return val_->getNodeID();
    }

    /// Whether the validation is considered trusted.
    bool
    trusted() const
    {
        return val_->isTrusted();
    }

    void
    setTrusted()
    {
        val_->setTrusted();
    }

    void
    setUntrusted()
    {
        val_->setUntrusted();
    }

    /// Whether the validation is full (not-partial)
    bool
    full() const
    {
        return val_->isFull();
    }

    /// Get the load fee of the validation if it exists
    std::optional<std::uint32_t>
    loadFee() const
    {
        return ~(*val_)[~sfLoadFee];
    }

    /// Get the cookie specified in the validation (0 if not set)
    std::uint64_t
    cookie() const
    {
        return (*val_)[sfCookie];
    }

    /// Extract the underlying STValidation being wrapped
    std::shared_ptr<STValidation>
    unwrap() const
    {
        return val_;
    }
};

/** Wraps a ledger instance for use in generic Validations LedgerTrie.

    The LedgerTrie models a ledger's history as a map from Seq -> ID. Any
    two ledgers that have the same ID for a given Seq have the same ID for
    all earlier sequences (e.g. shared ancestry). In practice, a ledger only
    conveniently has the prior 256 ancestor hashes available. For
    RCLValidatedLedger, we treat any ledgers separated by more than 256 Seq as
    distinct.
*/
class RCLValidatedLedger
{
public:
    using ID = LedgerHash;
    using Seq = LedgerIndex;
    struct MakeGenesis
    {
        explicit MakeGenesis() = default;
    };

    RCLValidatedLedger(MakeGenesis);

    RCLValidatedLedger(
        std::shared_ptr<Ledger const> const& ledger,
        beast::Journal j);

    /// The sequence (index) of the ledger
    Seq
    seq() const;

    /// The ID (hash) of the ledger
    ID
    id() const;

    /** Lookup the ID of the ancestor ledger

        @param s The sequence (index) of the ancestor
        @return The ID of this ledger's ancestor with that sequence number or
                ID{0} if one was not determined
    */
    ID
    operator[](Seq const& s) const;

    /// Find the sequence number of the earliest mismatching ancestor
    friend Seq
    mismatch(RCLValidatedLedger const& a, RCLValidatedLedger const& b);

    Seq
    minSeq() const;

private:
    ID ledgerID_;
    Seq ledgerSeq_;
    std::vector<uint256> ancestors_;
    beast::Journal j_;
};

/** Generic validations adaptor class for RCL

    Manages storing and writing stale RCLValidations to the sqlite DB and
    acquiring validated ledgers from the network.
*/
class RCLValidationsAdaptor
{
public:
    // Type definitions for generic Validation
    using Mutex = std::mutex;
    using Validation = RCLValidation;
    using Ledger = RCLValidatedLedger;

    RCLValidationsAdaptor(Application& app, beast::Journal j);

    /** Current time used to determine if validations are stale.
     */
    NetClock::time_point
    now() const;

    /** Attempt to acquire the ledger with given id from the network */
    std::optional<RCLValidatedLedger>
    acquire(LedgerHash const& id);

    beast::Journal
    journal() const
    {
        return j_;
    }

private:
    Application& app_;
    beast::Journal j_;
};

/// Alias for RCL-specific instantiation of generic Validations
using RCLValidations = Validations<RCLValidationsAdaptor>;

/** Handle a new validation

    Also sets the trust status of a validation based on the validating node's
    public key and this node's current UNL.

    @param app Application object containing validations and ledgerMaster
    @param val The validation to add
    @param source Name associated with validation used in logging
*/
void
handleNewValidation(
    Application& app,
    std::shared_ptr<STValidation> const& val,
    std::string const& source,
    BypassAccept const bypassAccept = BypassAccept::no,
    std::optional<beast::Journal> j = std::nullopt);

}  // namespace ripple

#endif
